//
//  FormEditor.m
//  iBasic
//
//  Created by Andrew Barry on 22/04/08.
//  Copyright 2008 Barry Computer Services. All rights reserved.
//

#import "FormEditor.h"
#import "ControlMetrics.h"
#import "ControlsDataSource.h"
#import "JavascriptForm.h"
#import "ControlPanel.h"
#import "CanvasView.h"
#import "jsonWriter.h"
#import "ControlRegistry.h"

#define kControlPasteboardType @"com.icodemac.controls"

@implementation FormEditor

- (id)initWithFrame:(NSRect)frame {
	self = [super initWithFrame:frame];
    if (self) {
		controls = [[NSMutableArray alloc] init];
    }

    [self registerForDraggedTypes:[NSArray arrayWithObjects: @"ControlsDataSource", nil]];
	formTitle = @"";
	formWidth = 400;
	formHeight = 300;
	frameAutosave = @"";
	return self;
}

-(void) postLoad {
//	[formview setFrame: NSMakeRect(0, 0, formWidth, formHeight)];
	for (int idx = 0; idx < [controls count]; idx++) {
		FormControl *ctl = [controls objectAtIndex:idx];
		[ctl postLoad];
	}
}

-(void) removeControl: (FormControl *) ctl {
	[controls removeObject:ctl];
	[[ctl view] removeFromSuperview];

	[overlay setSelectedObject: nil];
	[formDocument editorSelectedControl: nil];

	[[formDocument undoManager] registerUndoWithTarget:self selector:@selector(addControl:) object:ctl];

	[formDocument rebuildObjectPopup];
}

-(void) addControl: (FormControl *) ctl {
	[controls addObject:ctl];
	
	if ([ctl parent])
		[[[[ctl parent] view] contentView] addSubview:[ctl view]];
	else
		[self addSubview:[ctl view]];
	
	[overlay setSelectedObject: [ctl view]];

	[[formDocument undoManager] registerUndoWithTarget:self selector:@selector(removeControl:) object:ctl];

	[formDocument rebuildObjectPopup];
	[formDocument editorSelectedControl: ctl];
	[[ControlPanel sharedPanel] selectControl:ctl];
}

-(void) updateControlPanel {
	NSArray *selection = [overlay selection];
	if (!selection || [selection count] == 0)
		[[ControlPanel sharedPanel] selectControl:nil];
	else
		[[ControlPanel sharedPanel] selectControl:[self controlFromView: [selection objectAtIndex:0]]];
}

-(NSString *) projType {
	return projType;
}

-(BOOL) isWebForm {
	return projType != nil && [projType isEqualToString:@"webapp"];
}

-(NSArray *) controls {
	return controls;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
	if (draggingControl) {
		[self addControl: draggingControl];
		draggingControl = nil;
	}
	[overlay setVerticalAligns: nil];
	[overlay setHorizontalAligns: nil];

	return YES;
}

-(NSString *) getDefaultNameForType: (NSString *) typeName {
	int ctlIdx = 0;
	bool exists;
	NSString *fullName;
	do {
		ctlIdx++;
		exists = NO;
		fullName = [NSString stringWithFormat:@"%@%d", typeName, ctlIdx, nil];
		for (int idx = 0; idx < [controls count]; idx++) {
			FormControl *ctl = [controls objectAtIndex:idx];
			if ([[ctl controlName] isEqualToString:fullName]) {
				exists = true;
				break;
			}
		}
	} while (exists);
	return fullName;
}

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
	NSPoint pt = [sender draggingLocation];
	
	pt = [self convertPoint: pt fromView: nil];
	
	NSData *data = [[sender draggingPasteboard] dataForType: @"ControlsDataSource"];
	if (data == nil)
		return NO;
	
	NSString *controlType = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
	
	NSSize sz = [ControlPanel defaultControlSize: controlType];

	
	int width = sz.width;
	int height = sz.height;
	
	NSMutableDictionary *defn = [NSMutableDictionary new];
	[defn setValue:[NSNumber numberWithInt:pt.x - width / 2] forKey:@"left"];
	[defn setValue:[NSNumber numberWithInt:pt.y - height / 2] forKey:@"top"];
	[defn setValue:[NSNumber numberWithInt:width] forKey:@"width"];
	[defn setValue:[NSNumber numberWithInt:height] forKey:@"height"];
	[defn setValue:controlType forKey:@"type"];
	[defn setValue:[self getDefaultNameForType: controlType] forKey:@"name"];
	
	draggingControl = [FormControl loadFromSave:defn forEditor: self];
	[draggingControl setDocument:formDocument];
	[draggingControl setOverlay:overlay];
	
	if ([draggingControl parent])
		[[[[draggingControl parent] view] contentView] addSubview:[draggingControl view]];
	else
		[self addSubview:[draggingControl view]];
	
	return NSDragOperationCopy;
}

-(void)draggingExited:(id < NSDraggingInfo >)sender {
	if (draggingControl) {
		NSView *vw = [draggingControl view];
		[vw removeFromSuperview];
		[draggingControl release];
		draggingControl = nil;
	}
	[overlay setVerticalAligns: nil];
	[overlay setHorizontalAligns: nil];

}

-(void)alignControl: (FormControl *) ctl withFrame: (NSRect *) f withMask:(int) mask{
	NSRect editFrame = [self convertRect:*f fromView: [[ctl view] superview]];;
	
	NSDictionary *alignment = [ControlMetrics getControlAlignment:[ctl view] forFrame: editFrame];
	float vMinDelta = 10;
	float vOffset = 0.0;
//	float vAlign = 0.0;
	NSMutableDictionary *vAligns = [[NSMutableDictionary new] autorelease];;
	NSMutableDictionary *hAligns = [[NSMutableDictionary new] autorelease];;

	float hMinDelta = 10;
	float hOffset = 0.0;
	
	int count = [controls count];
	for (int idx = 0; idx < count; idx++) {
		FormControl *fctl = [controls objectAtIndex: idx];
		if (fctl != ctl) {
			
			editFrame = [self convertRect:[[fctl view] frame] fromView: [[fctl view] superview]];

			NSDictionary *falignment = [ControlMetrics getControlAlignment:[fctl view] forFrame: editFrame];

			for (int vidx = 0; vidx < 3; vidx++) {
				if (mask & (1 << (4 + vidx))) {
					NSString *key = (vidx == 0) ? @"top" : (vidx == 1) ? @"bottom" : @"baseline";
					NSNumber *ctop = [alignment valueForKey:key];
					NSNumber *ftop = [falignment valueForKey:key];
					
					if (ctop != nil && ftop != nil) {
						float cv = [ctop floatValue];
						float fv = [ftop floatValue];
						float dv = fabs(cv - fv);
						if (dv < 4 && dv <= vMinDelta) {
							if (dv < vMinDelta)
								[vAligns removeAllObjects];
							vMinDelta = dv;
							[vAligns setObject:ftop forKey:key];
							vOffset = fv - cv;
						}
					}
				}
			}
			
			
			for (int hidx = 0; hidx < 2; hidx++) {
				if (mask & (1 << (hidx))) {
					NSString *key = (hidx == 0) ? @"left" : @"right";
					NSNumber *cedge = [alignment valueForKey:key];
					NSNumber *fedge = [falignment valueForKey:key];
					
					if (cedge != nil && fedge != nil) {
						float cv = [cedge floatValue];
						float fv = [fedge floatValue];
						float dv = fabs(cv - fv);
						if (dv < 4 && dv <= hMinDelta) {
							if (dv < hMinDelta)
								[hAligns removeAllObjects];
							hMinDelta = dv;
							[hAligns setObject:fedge forKey:key];
							hOffset = fv - cv;
						}
					}
				}
			}
			
			
		}
	}

	*f = NSOffsetRect(*f, hOffset, vOffset);
	[overlay setVerticalAligns: vAligns];
	[overlay setHorizontalAligns: hAligns];
}

-(NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender {
	NSPoint pt = [sender draggingLocation];
	
	pt = [self convertPoint: pt fromView: nil];

	if (draggingControl != nil) {
		NSView *vw = [draggingControl view];
		NSRect f = [vw frame];
		f = NSOffsetRect(f, pt.x - f.origin.x - f.size.width / 2, pt.y - f.origin.y - f.size.height / 2);
		
		[self alignControl: draggingControl withFrame:  &f withMask: 0x77];

		[vw setFrame: f];
	}
	return NSDragOperationCopy;
}

-(void)drawRect:(NSRect)rect {
	if (hasBackgroundColor) {
		if (backgroundColor != nil)
			[backgroundColor setFill];
		else
			[[NSColor whiteColor] setFill];
	} else {
		[[NSColor windowBackgroundColor] setFill];
	}
	NSRectFill([self bounds]);
}

-(void) setOverlay: (FormEditorOverlay *) ovl {
	overlay = ovl;
}

- (void) setFormView: (FormView *) v {
	formview = v;
}

- (void) loadForm: (NSDictionary *) fromDict {
	formLoadEvent = [[fromDict objectForKey:@"Load"] retain];
	formUnloadEvent = [[fromDict objectForKey:@"Unload"] retain];
	formEnableControlsEvent = [[fromDict objectForKey:@"EnableControls"] retain];
	
	NSArray *saveControls = [fromDict objectForKey:@"controls"];
	int idx;

	projType = [fromDict objectForKey: @"projType"];
	if (!projType)
		projType = @"osxapp";
	[projType retain];
	
	formTitle = [[fromDict objectForKey: @"title"] retain];
	if (!formTitle)
		formTitle = @"Untitled";
	formWidth = [[fromDict objectForKey: @"width"] intValue];
	formHeight = [[fromDict objectForKey: @"height"] intValue];
	kind = [[fromDict objectForKey: @"kind"] intValue];
	frameAutosave = [[fromDict objectForKey: @"frameAutosave"] retain];
	
	if (!frameAutosave)
		frameAutosave = @"";
	
	[formview setFrame: NSMakeRect(0, 0, formWidth, formHeight)];

	if (!formWidth)
		formWidth = 400;
	if (!formHeight)
		formWidth = 300;
	
	canClose = [[fromDict objectForKey:@"canClose"] boolValue];
	canResize = [[fromDict objectForKey:@"canResize"] boolValue];
	formStartup = [[fromDict objectForKey:@"startup"] boolValue];
	
	hasBackgroundColor = [[fromDict objectForKey:@"hasBackgroundColor"] boolValue];
	
	NSString *strBackgroundColor = [fromDict objectForKey:@"backgroundColor"];
	if (strBackgroundColor) {
		NSArray *bits = [strBackgroundColor componentsSeparatedByString: @"/"];
		float r = [[bits objectAtIndex:0] floatValue];
		float g = [[bits objectAtIndex:1] floatValue];
		float b = [[bits objectAtIndex:2] floatValue];
		backgroundColor = [[NSColor colorWithCalibratedRed:r green:g blue: b alpha: 1.0f] retain];
	}
	
	for (idx = 0; idx < [saveControls count]; idx++) {
		NSDictionary *ctlDefn = [saveControls objectAtIndex: idx];
		FormControl *ctl = [FormControl loadFromSave:ctlDefn forEditor: self];
		[ctl setDocument:formDocument];
		[ctl setOverlay:overlay];
		[controls addObject:ctl];
		if ([ctl parent])
			[[[[ctl parent] view] contentView] addSubview:[ctl view]];
		else
			[self addSubview:[ctl view]];
	}
}

- (NSMutableDictionary *) saveForm {
	[formDocument commitTextView];

	NSMutableDictionary *formContent = [NSMutableDictionary new];
	NSMutableArray *saveControls = [NSMutableArray new];
	int idx;
	for (idx = 0; idx < [controls count]; idx++) {
		FormControl *ctl = [controls objectAtIndex:idx];
		[saveControls addObject:[ctl save]];
	}
	
	if (projType)
		[formContent setObject: projType forKey:@"projType"];
	else
		[formContent setObject: @"osxapp" forKey:@"projType"];
	
	[formContent setObject:saveControls forKey:@"controls"];
	[formContent setObject:[NSNumber numberWithInt:kind] forKey:@"kind"];
	[formContent setObject:[NSNumber numberWithInt:formWidth] forKey:@"width"];
	[formContent setObject:[NSNumber numberWithInt:formHeight] forKey:@"height"];
	[formContent setObject: formTitle forKey:@"title"];
	[formContent setObject: frameAutosave forKey:@"frameAutosave"];
	[formContent setObject: [NSNumber numberWithBool:formStartup] forKey:@"startup"];
	[formContent setObject: [NSNumber numberWithBool:canClose] forKey:@"canClose"];
	[formContent setObject: [NSNumber numberWithBool:canResize] forKey:@"canResize"];
	[formContent setObject: [NSNumber numberWithBool:hasBackgroundColor] forKey:@"hasBackgroundColor"];
	
	if (backgroundColor) {
		float rgb[4];
		[backgroundColor getComponents: rgb];
		[formContent setObject: [NSString stringWithFormat:@"%f/%f/%f",rgb[0], rgb[1], rgb[2]] forKey:@"backgroundColor"];
	}
	
	if (formLoadEvent != nil)
		[formContent setObject:formLoadEvent forKey:@"Load"];
	if (formUnloadEvent != nil)
		[formContent setObject:formUnloadEvent forKey:@"Unload"];
	if (formEnableControlsEvent != nil)
		[formContent setObject:formEnableControlsEvent forKey:@"EnableControls"];
	/*
	jsonWriter *writer = [[jsonWriter alloc] initForForm];
	[writer writeNSDictionary:formContent];
	NSString *result = [writer finish];
	[result writeToFile:@"/test.js" atomically:NO];
	*/
	return formContent;
}

-(void) setDocument: (JavascriptForm *) doc {
	formDocument = doc;
}

-(BOOL) acceptsFirstResponder {
	return YES;
}

-(NSView *)hitTest:(NSPoint)aPoint {
	return self;
}

- (BOOL) isFlipped {
	return YES;
}

int borderSize = 4;
int handleSize = 8;

-(void) keyDown: (NSEvent *) theEvent {
	int keyCode = [theEvent keyCode];
	int dx, dy;
	dx = dy = 0;
	switch (keyCode) {
		case 51:
			{
				NSArray *selection = [overlay selection];
				int idx, count = [selection count];
				for (idx = 0; idx < count; idx++) {
					NSView *vw = [selection objectAtIndex: idx];
					FormControl *ctl = [self controlFromView:vw];
					[self removeControl:ctl];
				}
			}
			return;
			break;
		case 123: // left
			dx = -1;
			break;
		case 124: // right
			dx = 1;
			break;
		case 125: // down
			dy = 1;
			break;
		case 126: // up
			dy = -1;
			break;
	}
	if (dx || dy) {
		if ([overlay hasSelection]) {
			NSArray *selection = [overlay selection];
			int idx, count = [selection count];
			for (idx = 0; idx < count; idx++) {
				NSView *vw = [selection objectAtIndex: idx];
				FormControl *ctl = [self controlFromView:vw];

				NSRect bounds = [vw frame];
				bounds = NSOffsetRect(bounds, dx, dy);
				[ctl moveControl:[NSValue valueWithRect:bounds]];
//				[overlay setNeedsDisplay:YES];
			}
		}
	}
}

-(FormControl *)controlFromView:(NSView *) view {
	if (!view)
		return nil;
	
	int count = [controls count];
	int idx;
	for (idx = 0; idx < count; idx++) {
		FormControl *ctl = [controls objectAtIndex: idx];
		if ([ctl view] == view)
			return ctl;
	}
	return nil;
}

-(FormControl *) controlByName: (NSString *) controlName {
	int count = [controls count];
	int idx;
	for (idx = 0; idx < count; idx++) {
		FormControl *ctl = [controls objectAtIndex: idx];
		if ([[ctl controlName] isEqualToString:controlName])
			return ctl;
	}
	return nil;
}

- (void)mouseDown:(NSEvent *)theEvent {
	NSPoint pt = [theEvent locationInWindow];
	NSPoint vpt = [self convertPoint:pt fromView:nil];

	dragHandle = NO;
	lastMouse = vpt;
	draggingSelection = NO;
	
	[[theEvent window] makeFirstResponder: self];
	
	// see if they hit a resize handle
	NSArray *selection = [overlay selection];
	int count = [selection count];
	int idx = 0;

	for (idx = 0; idx < count; idx++) {
		NSView *vw = [selection objectAtIndex:idx];
		NSRect baseframe = [vw frame];
		NSRect frame = [self convertRect:baseframe fromView:[vw superview]];
		
		FormControl *ctl = [self controlFromView:vw];
		[ctl setSavePos:baseframe];

		NSRect bottomLeft = NSMakeRect(frame.origin.x - handleSize, frame.origin.y - handleSize, handleSize, handleSize);
		if (NSPointInRect(vpt, bottomLeft)) {
			dragHandle = YES;
			handleControl = vw;
			handleCorner = 0;
			handleFrame = baseframe;
			return;
		}
			
		NSRect topLeft = NSMakeRect(frame.origin.x - handleSize, frame.origin.y + frame.size.height, handleSize, handleSize);
		if (NSPointInRect(vpt, topLeft)) {
			dragHandle = YES;
			handleControl = vw;
			handleCorner = 2;
			handleFrame = baseframe;
			return;
		}

		NSRect bottomRight = NSMakeRect(frame.origin.x + frame.size.width, frame.origin.y - handleSize, handleSize, handleSize);
		if (NSPointInRect(vpt, bottomRight)) {
			dragHandle = YES;
			handleControl = vw;
			handleCorner = 1;
			handleFrame = baseframe;
			return;
		}

		NSRect topRight = NSMakeRect(frame.origin.x + frame.size.width, frame.origin.y + frame.size.height, handleSize, handleSize);
		if (NSPointInRect(vpt, topRight)) {
			dragHandle = YES;
			handleControl = vw;
			handleCorner = 3;
			handleFrame = baseframe;
			return;
		}
	}
	
	// see if they hit a control
	count = [controls count];
	for (idx = count - 1; idx >= 0; idx--) {
		FormControl *ctl = [controls objectAtIndex:idx];
		NSView *vw = [ctl view];
		NSRect bounds = [vw frame];

		[ctl setSavePos:bounds];
		bounds = [self convertRect:bounds fromView:[vw superview]];
		// selected controls have an increased drag border
		if ([overlay isSelected:vw])
			bounds = NSInsetRect(bounds, -borderSize, -borderSize);
		
		if (NSPointInRect(vpt, bounds)) {
			if (![overlay isSelected: vw]) {
				
				if ([theEvent modifierFlags] & NSShiftKeyMask)
					[overlay addSelectedObject: vw];
				else
					[overlay setSelectedObject: vw];

				if ([[overlay selection] count] == 1) {
					[[ControlPanel sharedPanel] selectControl:ctl];
					[formDocument editorSelectedControl: ctl];
				}
			} else if ([theEvent modifierFlags] & NSShiftKeyMask) {
				//deselect control
				[overlay removeSelectedObject: vw];
				[self updateSelectedObject];
			}
			return;
		}
	}
	draggingSelection = YES;
	currentSelection = NSMakeRect(vpt.x, vpt.y, 0, 0);
	if (!([theEvent modifierFlags] & NSShiftKeyMask)) {
		[overlay setSelectedObject: nil];
		[[ControlPanel sharedPanel] selectControl:nil];
		[formDocument editorSelectedControl: nil];
	}
	initialSelection = [[NSArray arrayWithArray:[overlay selection]] retain];
}

-(void) updateSelectedObject {
	if ([[overlay selection] count] == 0) {
		[[ControlPanel sharedPanel] selectControl:nil];
		[formDocument editorSelectedControl: nil];
	} else if ([[overlay selection] count] == 1) {
		NSView *vw = [[overlay selection] objectAtIndex:0];
		FormControl *ctl = [self controlFromView:vw];
		[[ControlPanel sharedPanel] selectControl:ctl];
		[formDocument editorSelectedControl: ctl];
	} else {
	}
}

-(FormControl *) findContainerView: (NSPoint) pt forViews: (NSArray *) children {
	int count = [controls count];
	for (int idx = count - 1; idx >= 0; idx--) {
		FormControl *ctl = [controls objectAtIndex:idx];
		NSView *vw = [ctl view];

		if ([children containsObject:vw])
			continue;
		
		if ([vw isKindOfClass:[NSBox class]]) {
			NSPoint vpt;
			
			vpt = [self convertPoint:pt toView:[vw superview]];
			NSRect bounds = [vw frame];
			if (NSPointInRect(vpt, bounds)) {
				return ctl;
			}
		}
	}
	return nil;
}

-(void) selectControl: (FormControl *) ctl {
	if (!ctl)
		[overlay setSelectedObject: nil];
	else
		[overlay setSelectedObject: [ctl view]];
	[[ControlPanel sharedPanel] selectControl:ctl];
}

-(NSArray *) getFormEvents {
	return [NSArray arrayWithObjects:@"Load", @"EnableControls", @"Unload", nil];
}

+(NSArray *) getFormEvents {
	return [NSArray arrayWithObjects:@"Load", @"EnableControls", @"Unload", nil];
}

-(void) showInfo {
}

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
	return YES;
}

-(void) updateDragSelect {
	NSMutableArray *newSelection = [[NSMutableArray alloc] initWithArray:initialSelection];
	
	int count = [controls count];
	for (int idx = count - 1; idx >= 0; idx--) {
		FormControl *ctl = [controls objectAtIndex:idx];
		NSView *vw = [ctl view];
		if (![newSelection containsObject:vw]) {
			NSRect bounds = [vw frame];
			bounds = [self convertRect:bounds fromView:[vw superview]];
			if (bounds.origin.x < (currentSelection.origin.x + currentSelection.size.width) &&
				(bounds.origin.x + bounds.size.width) > currentSelection.origin.x &&
				bounds.origin.y < (currentSelection.origin.y + currentSelection.size.height) &&
				(bounds.origin.y + bounds.size.height) > currentSelection.origin.y)
				[newSelection addObject:vw];
		}
	}

	// now to sync up with existing selection
	int idx;
	// add any objects that aren't there
	NSArray *selection = [overlay selection];
	for (idx = 0; idx < [newSelection count]; idx++) {
		NSView *vw = [newSelection objectAtIndex:idx];
		if (![selection containsObject:vw])
			[overlay addSelectedObject:vw];
	}
	
	// remove any objects that are no longer there
	idx = 0;
	while (idx < [selection count]) {
		NSView *vw = [selection objectAtIndex:idx];
		if (![newSelection containsObject:vw])
			[overlay removeSelectedObject:vw];
		else
			idx++;
	}
	
	
	[newSelection release];
}

-(void) mouseDragged:(NSEvent *)theEvent {
	NSPoint pt = [theEvent locationInWindow];
	NSPoint vpt = [self convertPoint:pt fromView:nil];
	
	if (draggingSelection) {
		selEndMouse = vpt;
		NSRect r;
		if (lastMouse.x < selEndMouse.x) {
			r.origin.x = lastMouse.x;
			r.size.width = selEndMouse.x - lastMouse.x;
		} else {
			r.origin.x = selEndMouse.x;
			r.size.width = lastMouse.x - selEndMouse.x;
		}
		if (lastMouse.y < selEndMouse.y) {
			r.origin.y = lastMouse.y;
			r.size.height = selEndMouse.y - lastMouse.y;
		} else {
			r.origin.y = selEndMouse.y;
			r.size.height = lastMouse.y - selEndMouse.y;
		}
		currentSelection = r;
		[overlay setSelectionRect:r];
		[self updateDragSelect];
		return;
	// handling for corner resizing
	} else if (dragHandle) {
		int dx = vpt.x - lastMouse.x;
		int dy = vpt.y - lastMouse.y;
		NSRect newFrame = handleFrame;
		if (handleCorner & 1) { // right
			newFrame.size.width += dx;
			if (newFrame.size.width < 0)
				newFrame.size.width = 0;
		} else {
			if (dx < 0) {
				newFrame.size.width -= dx;
				newFrame.origin.x += dx;
			} else {
				if (dx > newFrame.size.width)
					dx = newFrame.size.width;
				newFrame.size.width -= dx;
				newFrame.origin.x += dx;
			}
		}

		if (handleCorner & 2) { // top
			newFrame.size.height += dy;
			if (newFrame.size.height < 0)
				newFrame.size.height = 0;
		} else {
			if (dy < 0) {
				newFrame.size.height -= dy;
				newFrame.origin.y += dy;
			} else {
				if (dy > newFrame.size.height)
					dy = newFrame.size.height;
				newFrame.size.height -= dy;
				newFrame.origin.y += dy;
			}
		}

		
		FormControl *ctl = [self controlFromView:handleControl];

		if (!([theEvent modifierFlags] & NSCommandKeyMask)) {

			if (handleCorner == 0) {
				[self alignControl: ctl withFrame:  &newFrame withMask: 0x51];
				newFrame.size.height = handleFrame.origin.y + handleFrame.size.height - newFrame.origin.y;
				newFrame.size.width = handleFrame.origin.x + handleFrame.size.width - newFrame.origin.x;
			} else if (handleCorner == 1) {
				[self alignControl: ctl withFrame:  &newFrame withMask: 0x52];
				newFrame.size.height = handleFrame.origin.y + handleFrame.size.height - newFrame.origin.y;
				int r = newFrame.origin.x +newFrame.size.width ;
				newFrame.size.width = r - handleFrame.origin.x;
				newFrame.origin.x = handleFrame.origin.x;
			} else if (handleCorner == 2) {
				[self alignControl: ctl withFrame:  &newFrame withMask: 0x21];
				int b = newFrame.origin.y + newFrame.size.height;
				newFrame.origin.y = handleFrame.origin.y;
				newFrame.size.height = b - newFrame.origin.y;
				newFrame.size.width = handleFrame.origin.x + handleFrame.size.width - newFrame.origin.x;
			} else if (handleCorner == 3) {
				[self alignControl: ctl withFrame:  &newFrame withMask: 0x22];
				int b = newFrame.origin.y + newFrame.size.height;
				newFrame.origin.y = handleFrame.origin.y;
				newFrame.size.height = b - newFrame.origin.y;
				int r = newFrame.origin.x +newFrame.size.width ;
				newFrame.size.width = r - handleFrame.origin.x;
				newFrame.origin.x = handleFrame.origin.x;
			}
		} else {
			[overlay setVerticalAligns:nil];
			[overlay setHorizontalAligns:nil];
		}
		
		[handleControl setFrame: newFrame];
		[overlay setNeedsDisplay:YES];

		if (ctl != nil) {
			[ControlPanel updateValue:[NSNumber numberWithInt:newFrame.origin.x] forProperty:@"left" forControl:ctl];
			[ControlPanel updateValue:[NSNumber numberWithInt:newFrame.origin.y] forProperty:@"top" forControl:ctl];
			[ControlPanel updateValue:[NSNumber numberWithInt:newFrame.size.width] forProperty:@"width" forControl:ctl];
			[ControlPanel updateValue:[NSNumber numberWithInt:newFrame.size.height] forProperty:@"height" forControl:ctl];
		}
		
		return;
	}
	
	if ([overlay hasSelection]) {
		NSArray *selection = [overlay selection];
		FormControl *parent = [self findContainerView: vpt forViews: selection];
		if (parent) {
			[overlay setHightlightView: [parent view]];
		} else {
			[overlay setHightlightView: nil];
		}
		
		int idx, count = [selection count];
		for (idx = 0; idx < count; idx++) {
			NSView *vw = [selection objectAtIndex: idx];
			
			FormControl *ctl = [self controlFromView:vw];
			bool parentIsSelected = false;

			for (FormControl *p = [ctl parent]; p; p = [p parent]) {
				bool has = [selection containsObject:[p view]];
				if (has) {
					parentIsSelected = true;
				}
			}
			
			if (parentIsSelected)
				continue;
			
			NSRect bounds = [vw frame];
			bounds = NSOffsetRect(bounds, vpt.x - lastMouse.x, vpt.y - lastMouse.y);

			if (count == 1) {
				if (!([theEvent modifierFlags] & NSCommandKeyMask)) {
					NSRect saveBounds = bounds;
					[self alignControl: ctl withFrame:  &bounds withMask: 0x77];
					vpt.x += bounds.origin.x - saveBounds.origin.x;
					vpt.y += bounds.origin.y - saveBounds.origin.y;
				} else {
					[overlay setVerticalAligns:nil];
					[overlay setHorizontalAligns:nil];
				}
			}

			[vw setFrame:bounds];

			if (ctl != nil) {
				[ControlPanel updateValue:[NSNumber numberWithInt:bounds.origin.x] forProperty:@"left" forControl:ctl];
				[ControlPanel updateValue:[NSNumber numberWithInt:bounds.origin.y] forProperty:@"top" forControl:ctl];
				[ControlPanel updateValue:[NSNumber numberWithInt:bounds.size.width] forProperty:@"width" forControl:ctl];
				[ControlPanel updateValue:[NSNumber numberWithInt:bounds.size.height] forProperty:@"height" forControl:ctl];
			}
			
		}
		[overlay setNeedsDisplay:YES];
	}
	lastMouse = vpt;
}

-(void) mouseUp:(NSEvent *)theEvent {
	NSView *containerView = [overlay highlightView];
	[overlay setHightlightView: nil];
	[overlay setVerticalAligns:nil];
	[overlay setHorizontalAligns:nil];
	if (draggingSelection) {
		draggingSelection = NO;
		[initialSelection release];
		initialSelection = nil;
		[overlay clearSelectionRect];
		[self updateSelectedObject];
	} if (dragHandle) {
		FormControl *ctl = [self controlFromView:handleControl];
		if ([ctl wasMoved])
			[[formDocument undoManager] registerUndoWithTarget:ctl selector:@selector(moveControl:) object:[NSValue valueWithRect:[ctl savePos]]];
	} else {
		FormControl *container = [self controlFromView:containerView];
		
		if ([overlay hasSelection]) {
			NSArray *selection = [overlay selection];
			int idx, count = [selection count];
			for (idx = 0; idx < count; idx++) {
				NSView *vw = [selection objectAtIndex: idx];
				FormControl *ctl = [self controlFromView:vw];
				
				if (ctl && [ctl wasMoved]) {
					if (container != [ctl parent] && count == 1) {
						FormControl *oldParent = [ctl parent];
						[ctl setParent:container];
						[ctl retain];
						[controls removeObject:ctl];
						[controls addObject:ctl];
						[ctl release];
						[ctl broadcastPosition];
						[[formDocument undoManager] registerUndoWithTarget:ctl selector:@selector(moveAndReparentControl:) object:[NSArray arrayWithObjects: oldParent ? oldParent : (id)[NSNull null], [NSValue valueWithRect:[ctl savePos]], nil]];

					} else {
						[[formDocument undoManager] registerUndoWithTarget:ctl selector:@selector(moveControl:) object:[NSValue valueWithRect:[ctl savePos]]];
					}
				}
				
			}
		}
	}
}

-(void) commitSource: (NSString *) content forObject: (id) obj forEvent: (NSString *) eventName {
	if (!obj) {
		if ([eventName isEqualToString: @"Load"]) {
			[formLoadEvent autorelease];
			formLoadEvent = [content retain];
		} else if ([eventName isEqualToString: @"Unload"]) {
			[formUnloadEvent autorelease];
			formUnloadEvent = [content retain];
		} else if ([eventName isEqualToString: @"EnableControls"]) {
			[formEnableControlsEvent autorelease];
			formEnableControlsEvent = [content retain];
		}
	} else {
		[obj commitSource: content forEvent: eventName];
	}
}

-(NSString *) sourceForObject: (id) obj forEvent: (NSString *) eventName {
	if (!obj) {
		if ([eventName isEqualToString: @"Load"])
			return formLoadEvent;
		else if ([eventName isEqualToString: @"Unload"])
			return formUnloadEvent;
		else if ([eventName isEqualToString: @"EnableControls"])
			return formEnableControlsEvent;
		else
			return nil;
	} else {
		return [obj sourceForEvent: eventName];
	}
}

-(id) valueForProperty: (NSString *) name {
	if ([name isEqualToString:@"width"])
		return [NSNumber numberWithInt:formWidth];
	else if ([name isEqualToString:@"height"])
		return [NSNumber numberWithInt:formHeight];
	else if ([name isEqualToString:@"canResize"])
		return  [NSNumber numberWithInt: canResize];
	else if ([name isEqualToString:@"startup"])
		return  [NSNumber numberWithInt: formStartup];
	else if ([name isEqualToString:@"canClose"])
		return  [NSNumber numberWithInt: canClose];
	else if ([name isEqualToString:@"kind"])
		return  [NSNumber numberWithInt: kind];
	else if ([name isEqualToString:@"hasBackgroundColor"])
		return  [NSNumber numberWithInt: hasBackgroundColor];
	else if ([name isEqualToString:@"backgroundColor"])
		return  backgroundColor;
	else if ([name isEqualToString:@"title"])
		return formTitle;
	else if ([name isEqualToString:@"frameAutosave"])
		return frameAutosave;

	return nil;
}

-(void) setValue: (id) val forProperty:(NSString*) name {
	if ([name isEqualToString:@"width"]) {
		formWidth = [val intValue];
		[formview setFrame: NSMakeRect(0, 0, formWidth, formHeight)];
	} else if ([name isEqualToString:@"height"]) {
		formHeight = [val intValue];
		[formview setFrame: NSMakeRect(0, 0, formWidth, formHeight)];
	} else if ([name isEqualToString:@"kind"]) {
		kind = [val intValue];
	} else if ([name isEqualToString:@"canResize"])
		canResize = [val boolValue];
	else if ([name isEqualToString:@"canClose"])
		canClose = [val boolValue];
	else if ([name isEqualToString:@"startup"])
		formStartup = [val boolValue];
	else if ([name isEqualToString:@"hasBackgroundColor"]) {
		hasBackgroundColor = [val boolValue];
		[self setNeedsDisplay:YES];
	} else if ([name isEqualToString:@"backgroundColor"]) {
		[backgroundColor autorelease];
		backgroundColor = [[(NSColor *) val colorUsingColorSpace: [NSColorSpace genericRGBColorSpace]] retain];
		[self setNeedsDisplay:YES];
	} else if ([name isEqualToString:@"title"]) {
		formTitle  = [val retain];
	} else if ([name isEqualToString:@"frameAutosave"]) {
		frameAutosave  = [val retain];
	}
}

-(BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem {
	SEL theAction = [anItem action];
	
	if (theAction == @selector(copy:) ||
		theAction == @selector(cut:) ||
		theAction == @selector(delete:))
		return [overlay hasSelection];

	if (theAction == @selector(paste:)) {
		NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
		return  nil != [pasteboard availableTypeFromArray:[NSArray arrayWithObject: kControlPasteboardType]];
	}
	
	return YES;
//	return [super validateUserInterfaceItem: anItem];
}

-(void) copy: (id) sender {
	NSMutableArray *result = [NSMutableArray new];
	NSArray *ctlSel = [overlay selection];
	for (int idx = 0; idx < [ctlSel count]; idx++) {
		NSView *vw = [ctlSel objectAtIndex:idx];
		FormControl *ctl = [self controlFromView:vw];
		NSDictionary *ctlsave = [ctl save];
		[result addObject:ctlsave];
	}

	NSPasteboard *pb = [NSPasteboard generalPasteboard];
	[pb declareTypes:[NSArray arrayWithObject: kControlPasteboardType] owner:nil];
    [pb setPropertyList:result forType:kControlPasteboardType];
}

-(void) cut: (id) sender {
	[self copy: sender];
	[self delete: sender];
}

-(void) paste: (id) sender {
	NSPasteboard *pb = [NSPasteboard generalPasteboard];
	if ( nil == [pb availableTypeFromArray:[NSArray arrayWithObject: kControlPasteboardType]])
		return;
	NSArray *defns = [pb propertyListForType:kControlPasteboardType];
	for (int idx = 0; idx < [defns count]; idx++) {
		NSDictionary *ctldefn = [defns objectAtIndex:idx];
		FormControl *ctl = [FormControl loadFromSave: ctldefn forEditor: self];
		[ctl setOverlay: overlay];
		[ctl setDocument: formDocument];
		[self addControl: ctl];
	}
}

-(void) delete: (id) sender {
	NSArray *ctlSel = [overlay selection];
	for (int idx = 0; idx < [ctlSel count]; idx++) {
		NSView *vw = [ctlSel objectAtIndex:idx];
		FormControl *ctl = [self controlFromView:vw];

		[self removeControl:ctl];
	}
}

@end

@implementation FormEditorOverlay

- (id)initWithFrame:(NSRect)frame {
	self = [super initWithFrame:frame];
    if (self) {
		selection = [[NSMutableArray alloc] initWithCapacity:1];
    }
    return self;
}

- (void) addSelectedObject: (id) obj {
	[selection addObject:obj];
	[self setNeedsDisplay:YES];
}

- (void) removeSelectedObject: (id) obj {
	[selection removeObject:obj];
	[self setNeedsDisplay:YES];
}

- (void) setSelectedObject: (id) obj {
	[selection removeAllObjects];
	if (obj)
		[selection addObject:obj];
	[self setNeedsDisplay:YES];
}

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
	return YES;
}

-(NSView *) highlightView {
	return highlightView;
}

-(void) setHightlightView: (NSView *) view {
	if (highlightView != view) {
		highlightView = view;
		[self setNeedsDisplay: YES];
	}
}

-(void) setVerticalAligns: (NSDictionary *) aligns {
	[vAligns autorelease];
	vAligns = [aligns retain];
	[self setNeedsDisplay: YES];
}

-(void) setHorizontalAligns: (NSDictionary *) aligns {
	[hAligns autorelease];
	hAligns = [aligns retain];
	[self setNeedsDisplay: YES];
}

- (BOOL) isSelected: (id) obj {
	return [selection containsObject:obj];
}

- (BOOL) hasSelection {
	return [selection count] > 0;
}

- (NSArray *) selection {
	return selection;
}

- (BOOL) isFlipped {
	return YES;
}

void drawHandle(NSRect r) {
	NSRectFill(r);
}

void drawHorizAlign(NSView *sel, NSRect frame, NSDictionary *aligns, NSString *key) {
	NSNumber *n = [aligns objectForKey:key];
	if (n == nil)
		return;
	int y = [n intValue];
	NSRectFill(NSMakeRect(frame.origin.x, y, frame.size.width, 1));
}

- (void)drawRect:(NSRect)rect {
	int idx, count = [selection count];
	NSRect overlayBounds = [self bounds];
	
	for (idx = 0; idx < count; idx++) {
		NSView *sel = [selection objectAtIndex:idx];
		NSRect frame = [sel frame];
		frame = [self convertRect: frame fromView: [sel superview]];
		
		[[NSColor colorWithDeviceRed:0.6 green:0.6 blue:0.8 alpha:1] setFill];
		NSRectFill(NSMakeRect(frame.origin.x, frame.origin.y - borderSize, frame.size.width, borderSize));
		NSRectFill(NSMakeRect(frame.origin.x-borderSize, frame.origin.y, borderSize, frame.size.height));
		NSRectFill(NSMakeRect(frame.origin.x, frame.origin.y + frame.size.height, frame.size.width, borderSize));
		NSRectFill(NSMakeRect(frame.origin.x+frame.size.width, frame.origin.y, borderSize, frame.size.height));
		
		[[NSColor blackColor] setFill];
		drawHandle(NSMakeRect(frame.origin.x - handleSize, frame.origin.y - handleSize, handleSize, handleSize));
		drawHandle(NSMakeRect(frame.origin.x - handleSize, frame.origin.y + frame.size.height, handleSize, handleSize));
		drawHandle(NSMakeRect(frame.origin.x + frame.size.width, frame.origin.y - handleSize, handleSize, handleSize));
		drawHandle(NSMakeRect(frame.origin.x + frame.size.width, frame.origin.y + frame.size.height, handleSize, handleSize));
	}
	
	if (highlightView) {
		[[NSColor colorWithDeviceRed:0.6 green:0.6 blue:0.8 alpha:1] setFill];
		NSRect frame = [highlightView frame];
		NSRectFill(NSMakeRect(frame.origin.x, frame.origin.y - borderSize, frame.size.width + borderSize, borderSize));
		NSRectFill(NSMakeRect(frame.origin.x-borderSize, frame.origin.y-borderSize, borderSize, frame.size.height + borderSize));
		NSRectFill(NSMakeRect(frame.origin.x-borderSize, frame.origin.y + frame.size.height, frame.size.width + borderSize, borderSize));
		NSRectFill(NSMakeRect(frame.origin.x+frame.size.width, frame.origin.y, borderSize, frame.size.height + borderSize));
	}
	
	if (hAligns) {
		[[NSColor colorWithDeviceRed:0.6 green:0.6 blue:0.8 alpha:1] setFill];
		for (int hidx = 0; hidx < 2; hidx++) {
			NSString *key = (hidx == 0) ? @"left" : @"right";
			NSNumber *v = [hAligns objectForKey:key];
			if (v) {
				float fv = [v floatValue];
				NSRectFill(NSMakeRect(fv, 0, 1, overlayBounds.size.height));
			}
		}
	}
	
	if (vAligns) {
		[[NSColor colorWithDeviceRed:0.6 green:0.6 blue:0.8 alpha:1] setFill];
		for (int vidx = 0; vidx < 3; vidx++) {
			NSString *key = (vidx == 0) ? @"top" : (vidx == 1) ? @"bottom" : @"baseline";
			NSNumber *v = [vAligns objectForKey:key];
			if (v) {
				float fv = [v floatValue];
				NSRectFill(NSMakeRect(0, fv, overlayBounds.size.width, 1));
			}
		}
	}
	
	if (hasSelectionRect) {
		[[NSColor colorWithDeviceRed: 0 green: 0 blue: 0 alpha: 0.1] setFill];
		[[NSColor colorWithDeviceRed: 0 green: 0 blue: 0 alpha: 0.1] set];
		NSRect r = NSOffsetRect(selectionRect, 0.5, 0.5);

		[NSBezierPath fillRect: r];
		[NSBezierPath strokeRect: r];
	}
}

-(void) setSelectionRect: (NSRect) r {
	selectionRect = r;
	hasSelectionRect = YES;
	[self setNeedsDisplay:YES];
}

-(void) clearSelectionRect {
	hasSelectionRect = NO;
	[self setNeedsDisplay:YES];
}

@end

